import 'dart:convert';
import 'dart:developer';

import 'package:manager/exts/plugins_ext.dart';
import 'package:manager/http/token_interceptor.dart';
import 'package:manager/models/page_model.dart';
import 'package:bot_toast/bot_toast.dart';
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:flutter/foundation.dart';
import 'package:manager/configs/app_data.dart';
import 'package:manager/exts/app_ext.dart';
import 'package:manager/http/entity/base_response.dart';
import 'package:manager/http/entity/entity_factory.dart';

import '../models/token_model.dart';
import 'app_exceptions.dart';
import 'entity/base_list_entity.dart';
import 'entity/base_page_entity.dart';
import 'entity/request_method.dart';
import 'error_interceptor.dart';
import 'urls.dart';

class DioManager {
  static const CONNECT_TIMEOUT = Duration(seconds: 30);
  static const RECEIVE_TIMEOUT = Duration(seconds: 30);
  static final DioManager _share = DioManager._internal();

  factory DioManager() => _share;
  Dio? dio;

  DioManager._internal() {
    if (dio == null) {
      var options = BaseOptions(
          connectTimeout: CONNECT_TIMEOUT,
          receiveTimeout: RECEIVE_TIMEOUT,
          followRedirects: true,
          baseUrl: !kDebugMode ? Urls.PUB_BASE_URL : Urls.BASE_URL,
          headers: {'Content-Type': 'application/json;charset=UTF-8'});
      dio = Dio(options);
      dio!.interceptors.add(ErrorInterceptor());
      dio!.interceptors.add(CookieManager(CookieJar()));
      dio!.interceptors.add(TokenInterceptor());
    }
  }

  void cancleRequest() {
    var token = CancelToken();
  }

  /// 基本请求方法
  Future request<T>(String path,
      {Method method = Method.POST,
      required Map<String, dynamic> params,
      Function(T)? onSuccess,
      Function(AppException)? onError,
      String? notify = '加载中...',
      bool show = false}) async {
    if (show) {
      showLoading(notify);
    }
    loggerMsg('requestUrl:${dio?.options.baseUrl} $path---$params');

    try {
      var options = Options();
      var response = await dio!.request(path,
          data: params,
          options: options.copyWith(
            method: MethodValues[method],
          ));

      var baseEntity;
      var decodeString = response.data;
      log(json.encode(decodeString));
      var model =
          (decodeString is String) ? json.decode(decodeString) : decodeString;

      if (model['code'] == 0) {
        if (T.toString() == 'String') {
          onSuccess?.call(model['data']);
        } else {
          baseEntity = EntityFactory.convertOBJ<T>(model['data']);
          onSuccess?.call(baseEntity);
        }
      } else {
        cancelLoading();
        if (onError != null) {
          onError(AppException(code: model['code'], msg: model['msg']));
        }
        if (model['code'] == 401) {
          showToast('登录已过期,请重新登录');
        }
      }
      if (show) {
        BotToast.closeAllLoading();
      }
    } on DioException catch (e) {
      onError?.call(AppException.create(e));
      BotToast.closeAllLoading();
      showLog('request|Exception: ${e.toString()}');
    } on NoSuchMethodError catch (e) {
      onError?.call(AppException(code: -1, msg: '解析出错'));
      BotToast.closeAllLoading();
      showLog('request|Exception: ${e.toString()}');
    } on Exception catch (e) {
      BotToast.closeAllLoading();
      showLog('request|Exception: ${e.toString()}');
      onError?.call(AppException(code: -1, msg: e.toString()));
    } finally {}
  }

  /// 基本列表请求方法
  Future requestList<T>(String path,
      {Method method = Method.POST,
      required Map<String, dynamic> params,
      Function(List<T>)? onSuccess,
      Function(AppException)? onError,
      String? notify = '加载中...',
      bool show = false}) async {
    loggerMsg('requestUrl: $path');
    loggerMsg('入参: $params');
    if (show) {
      showLoading(notify);
    }
    var options = AppData.accessToken.isNotEmpty
        ? Options(headers: {'Authorization': 'Bearer ${AppData.accessToken}'})
        : Options();
    try {
      var response = await dio!.request(path,
          data: params,
          options: options.copyWith(method: MethodValues[method]));
      var decodeString = response.data;
      log(json.encode(decodeString));
      var baseEntity = BaseListEntity<T>.fromJson(
          (decodeString is String) ? json.decode(decodeString) : decodeString);
      if (baseEntity.isSuccess) {
        if (onSuccess != null) {
          onSuccess(baseEntity.data);
        }
      } else {
        if (onError != null) {
          onError(AppException(code: baseEntity.code, msg: baseEntity.msg));
        }
        if (baseEntity.code == 424) {
          showToast('登录已过期,请重新登录');
          logout();
        }
      }
      if (show) {
        BotToast.closeAllLoading();
      }
    } on DioException catch (e) {
      if (onError != null) {
        onError.call(AppException.create(e));
      }
      BotToast.closeAllLoading();
    } on Exception catch (e) {
      BotToast.closeAllLoading();
      onError?.call(AppException(code: -1, msg: e.toString()));
      showLog('requestList|Exception: ${e.toString()}');
    } finally {
      BotToast.closeAllLoading();
    }
  }


  /// 基本列表请求方法
  Future requestPage<T>(String path,
      {Method method = Method.POST,
      required Map<String, dynamic> params,
      Function(PageModel<T>)? onSuccess,
      Function(AppException)? onError,
      String? notify = '加载中...',
      bool show = false}) async {
    loggerMsg('requestUrl: $path');
    loggerMsg('入参: $params');
    if (show) {
      showLoading(notify);
    }
    var options = AppData.accessToken.isNotEmpty
        ? Options(headers: {'Authorization': 'Bearer ${AppData.accessToken}'})
        : Options();
    try {
      var response = await dio!.request(path,
          data: params,
          options: options.copyWith(method: MethodValues[method]));
      var decodeString = response.data;
      log(json.encode(decodeString));
      var baseEntity = BasePageEntity<T>.fromJson(
          (decodeString is String) ? json.decode(decodeString) : decodeString);
      if (baseEntity.isSuccess) {
        if (onSuccess != null) {
          onSuccess(baseEntity.data);
        }
      } else {
        if (onError != null) {
          onError(AppException(code: baseEntity.code, msg: baseEntity.msg));
        }
        if (baseEntity.code == 424) {
          showToast('登录已过期,请重新登录');
          logout();
        }
      }
      if (show) {
        BotToast.closeAllLoading();
      }
    } on DioException catch (e) {
      if (onError != null) {
        onError.call(AppException.create(e));
      }
      BotToast.closeAllLoading();
    } on Exception catch (e) {
      BotToast.closeAllLoading();
      onError?.call(AppException(code: -1, msg: e.toString()));
      showLog('requestList|Exception: ${e.toString()}');
    } finally {
      BotToast.closeAllLoading();
    }
  }

  /// 基本请求方法,返回的数据不在data中,自己写解析类
  Future requestAll<T extends BaseResponse>(String path,
      {Method method = Method.POST,
      required Map<String, dynamic> params,
        Map<String, dynamic>? headers,
      Function(T)? onSuccess,
      Function(AppException)? onError,
      String? notify = '加载中...',
      bool show = false}) async {
    if (show) {
      showLoading(notify);
    }
    loggerMsg('requestUrl: $path');
    loggerMsg(params);
    try {
      var options = Options();
      var response = await dio!.request(path,
          data: params,
          options: options.copyWith(
            method: MethodValues[method],
            headers: headers
          ));
      var decodeString = response.data;
      showLog('解密数据:$decodeString');
      var model = EntityFactory.convertOBJ<T>(
          (decodeString is String) ? json.decode(decodeString) : decodeString);
      if (model != null && model.isSuccess) {
        if (onSuccess != null) {
          onSuccess(model);
        }
      } else {
        if (onError != null) {
          onError(AppException(code: model?.code ?? -1, msg: model?.msg ?? ''));
        }
        if (model?.code == 401) {
          showToast('登录已过期,请重新登录');
        }
      }

      if (show) {
        BotToast.closeAllLoading();
      }
    } on DioException catch (e) {
      BotToast.closeAllLoading();
      onError?.call(AppException.create(e));
      showLog('request|Exception: ${e.toString()}');
    } on Exception catch (e) {
      BotToast.closeAllLoading();
      showLog('request|Exception: ${e.toString()}');
      onError?.call(AppException(code: -1, msg: e.toString()));
    } finally {
      BotToast.closeAllLoading();
    }
  }

  /// 获取token信息
  Future token<T>(String path,
      {Method method = Method.POST,
      required Map<String, dynamic> params,
        Map<String, dynamic>? headers,
      Function(T)? onSuccess,
      Function(AppException)? onError,
      String? notify = '加载中...',
      bool show = false}) async {
    if (show) {
      showLoading(notify);
    }
    try {
      var options = Options();
      var response = await dio!.request(path,
          data: params,
          options: options.copyWith(
            method: MethodValues[method],
            headers: {
              'Content-Type': 'application/x-www-form-urlencoded',
              'Authorization': 'Basic YWdybzphZ3JvYXBw'
            }
          ));
      var decodeString = response.data;
      showLog('解密数据:$decodeString');
      if(decodeString==null){
        onError?.call(AppException(code: -1, msg: '获取登录信息失败'));
      }else{
        var model = EntityFactory.convertOBJ<T>(
            (decodeString is String) ? json.decode(decodeString) : decodeString);
        if(model!=null) {
          onSuccess?.call(model);
        }else{
          onError?.call(AppException(code: -1, msg: '获取登录信息失败'));
        }
      }

      if (show) {
        BotToast.closeAllLoading();
      }
    } on DioException catch (e) {
      BotToast.closeAllLoading();
      onError?.call(AppException.create(e));
      showLog('request|Exception: ${e.toString()}');
    } on Exception catch (e) {
      BotToast.closeAllLoading();
      showLog('request|Exception: ${e.toString()}');
      onError?.call(AppException(code: -1, msg: e.toString()));
    } finally {
      BotToast.closeAllLoading();
    }
  }

  /// 上传单张图片
  Future uploadSingleFile<T extends BaseResponse>(String path, String filePath,
      {Method method = Method.POST,
      required Map<String, dynamic> params,
      Function(T)? onSuccess,
      Function(AppException)? onError,
      String? notify = '加载中...',
      bool show = false}) async {
    if (show) {
      showLoading(notify);
    }
    loggerMsg('requestUrl: $path');
    try {
      var options = AppData.accessToken.isNotEmpty
          ? Options(headers: {'Authorization': 'Bearer ${AppData.accessToken}'})
          : Options();

      var formData = FormData.fromMap({
        'file': await MultipartFile.fromFile(
          filePath, //图片路径
          filename: filePath.substring(filePath.lastIndexOf('/') + 1), //图片名称
        )
      });

      var response = await dio!.post(path,
          queryParameters: params,
          data: formData,
          options: options.copyWith(
            method: MethodValues[method],
          ), onSendProgress: (count, total) {
        showLog('uploading--$count---$total');
      });
      var model;
      var decodeString = response.data;
      showLog('解密数据:$decodeString');
      model = EntityFactory.convertOBJ<T>(
          (decodeString is String) ? json.decode(decodeString) : decodeString);
      if (model != null && model.code == 0) {
        if (onSuccess != null) {
          onSuccess(model);
        }
      } else {
        if (onError != null) {
          onError(AppException(code: model?.code ?? -1, msg: model?.msg ?? ''));
        }
      }

      if (show) {
        BotToast.closeAllLoading();
      }
    } on DioException catch (e) {
      if (onError != null) {
        onError?.call(AppException.create(e));
      }
      BotToast.closeAllLoading();
      showLog('request|Exception: ${e.toString()}');
    } on Exception catch (e) {
      BotToast.closeAllLoading();
      showLog('request|Exception: ${e.toString()}');
      onError?.call(AppException(code: -1, msg: e.toString()));
    } finally {
      BotToast.closeAllLoading();
    }
  }

  /// 上传图片,可以上传多张或单张
  Future uploadFile<T extends BaseResponse>(String path, FormData formData,
      {Method method = Method.POST,
      Map<String, dynamic>? params,
      Function(T)? onSuccess,
      Function(AppException)? onError,
      String? notify = '加载中...',
      bool show = false}) async {
    if (show) {
      showLoading(notify);
    }
    loggerMsg('requestUrl: $path');
    try {
      var options = AppData.accessToken.isNotEmpty
          ? Options(headers: {'Authorization': 'Bearer ${AppData.accessToken}'})
          : Options();

      var response = await dio!.post(path,
          queryParameters: params ?? {},
          data: formData,
          options: options.copyWith(
            method: MethodValues[method],
          ),
          onSendProgress: (count, total) {});

      var origin = response.data;

      ///如果数据没有加密,则直接使用,如果加密进行解密操作
      var model = EntityFactory.convertOBJ<T>(
          (origin is String) ? json.decode(origin) : origin);
      if (model != null && model.isSuccess) {
        if (onSuccess != null) {
          onSuccess(model);
        }
      } else {
        if (onError != null) {
          onError(AppException(code: model?.code ?? -1, msg: model?.msg ?? ''));
        }
      }

      if (show) {
        BotToast.closeAllLoading();
      }
    } on DioException catch (e) {
      if (onError != null) {
        onError.call(AppException.create(e));
      }
      BotToast.closeAllLoading();
      showLog('request|Exception: ${e.toString()}');
    } on Exception catch (e) {
      BotToast.closeAllLoading();
      showLog('request|Exception: ${e.toString()}');
      onError?.call(AppException(code: -1, msg: e.toString()));
    } finally {
      BotToast.closeAllLoading();
    }
  }
}
